package com.afdor.rws.event.channel.client.producer;

import com.afdor.rws.core.extension.ExtensionLoader;
import com.afdor.rws.event.Event;
import com.afdor.rws.event.Serialization;
import com.afdor.rws.event.channel.filter.EventIntercept;
import com.afdor.rws.event.channel.filter.EventInterceptChain;
import com.afdor.rws.event.config.EventConfigUtils;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NonNull;
import lombok.ToString;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;

/**
 * 生存者抽象实现
 *
 * @author 悭梵
 * @date Created in 2018-08-15 10:52
 */
@ToString
@EqualsAndHashCode
public abstract class AbstractEventProducer implements EventProducer {

    private volatile boolean init = Boolean.FALSE;
    @Getter
    @NonNull
    private volatile Serialization serialization;
    @NonNull
    private volatile EventInterceptChain interceptChain;

    @Override
    public AbstractEventProducer init() {
        if (interceptChain == null) {
            synchronized (this) {
                if (interceptChain == null) {
                    interceptChain = ExtensionLoader
                            .getInstance(EventInterceptChain.class)
                            .getExtensionOrDefault(EventConfigUtils
                                    .getProperty(EventConfigUtils.EVENT_PRODUCER_INTERCEPT_CHAIN_NAME, "default"));
                    init = Boolean.TRUE;
                }
            }
        }
        return this;
    }

    @Override
    public void destroy() {
        if (interceptChain != null) {
            interceptChain.destroy();
        }
    }

    @Override
    public AbstractEventProducer setSerialization(@NonNull Serialization serialization) {
        this.serialization = serialization;
        return this;
    }

    @Override
    public final AbstractEventProducer sendEvent(@NonNull Event event) {
        checkup();
        EventInterceptChain interceptChain = this.interceptChain.copy();
        interceptChain.doIntercept(event);
        if (!interceptChain.isIntercept()) {
            doSendEvent(event);
        }
        return this;
    }

    @Override
    public AbstractEventProducer addIntercept(@NonNull EventIntercept addIntercept) {
        checkup();
        this.interceptChain.addIntercept(addIntercept);
        return this;
    }

    /**
     * 子类覆盖执行发送事件业务
     *
     * @param event
     */
    protected abstract void doSendEvent(Event event);

    /**
     * 验证初始话是否成功
     */
    private void checkup() {
        int waitCount = 0;
        while (!this.init || this.interceptChain == null || waitCount == 100) {
            LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(10));
            waitCount++;
        }
        if (!this.init || this.interceptChain == null) {
            throw new IllegalStateException("please execute the init method first.");
        }
    }
}
